from not_so_free_marker import statics, string_helper, proc_func
from not_so_free_marker.enums import MarkTypeEnum, VarTypeEnum
from not_so_free_marker.models import RegMatchVar, Variant
from not_so_free_marker.statics import regVar_func, regVar_var, regIfHead, regElseIf, regNumber
from not_so_free_marker.string_helper import ExpressProc


def get_var_tags(template: str):
    var_tags = []
    for v in statics.regListHead.finditer(template):
        var_item = RegMatchVar()
        var_item.position = v.start(0)
        var_item.position_end = v.end(0)
        var_item.tagStr = v.group(1)
        var_item.type = MarkTypeEnum.REG_VAR
        var_item.var = v.group(2)
        var_tags.append(var_item)

    return var_tags


# 把变量替换成json里的数值
def process(template: str, data: map):
    # 处理变量是用游标查找${的方式,
    # 处理表达式是用正则查找标签,
    # 之所以处理方式不统一,是因为变量这块是先写的.
    vared_str = __private_process_value(template, data)
    return __private_process_express(vared_str, data)


# 检查标记是否合法
def check_var_tags(template: str):
    if (len(template) < 3):
        return True
    idx = 1
    var_tag_stack = []
    while idx < len(template):
        if template[idx - 1:idx + 1] == "${":
            var_tag_stack.append(idx)
        elif template[idx] == "}":
            if len(var_tag_stack) > 0:
                var_tag_stack.pop()
            else:
                raise Exception("变量标记没有配对,当前位置{0}".format(idx))
        if len(var_tag_stack) > 1:
            raise Exception("变量开始标记不能重复,重复位置{0}".format(idx))

        idx += 1
    else:
        if len(var_tag_stack) > 0:
            raise Exception("变量标记没有配对")

    return True


# 处理整个模板的内容.
# 查找所有${}标记,这次不用正则,用游标.(因为这次规则比较简单,找到${和}即可)
# 用__private_process_one_var处理单个变量/表达式.

def __private_process_value(template: str, data: map):
    if len(template) < 3:
        return template

    idx = 1
    tag_start_idx = idx
    tag_end_index = 0
    str_list = []

    while idx < len(template):
        if template[idx - 1:idx + 1] == "${":
            tag_start_idx = idx
            # 把之前的模板弄过去
            str_list.append(template[tag_end_index:tag_start_idx - 1])
        elif template[idx] == "}":
            # +1是为了跳过"${",不把开始标记加入到处理后的文本
            processed_var = __private_process_one_var(template[tag_start_idx + 1: idx].strip(), data)
            str_list.append(processed_var)
            # 加一是为了跳过"}",不把结束标记加入到处理后的文本
            tag_end_index = idx + 1
        idx += 1
    else:
        # 如果最后一个字符不是变量标记中的结束"}",那么还要把最后一个变量后面的部分附加上去
        if tag_end_index < len(template):
            str_list.append(template[tag_end_index:])

    return "".join(str_list)


# 处理单个括号${},这里面临的问题是如何把形如a.b.c(x.y.z)这样的内容区分开,观察得知函数后面都有括号,
# 步骤
# 1 选择出所有函数,放入函数列表list_func中,这里认为函数都是形似"a.b.c("形式的
# 2 去掉函数后面的尾巴,保留a.b.c
# 3 选择所有变量,这里认为变量是形似x.y.z形式的
# 4 如果变量不在函数列表list_func中,说明它确实是变量,就调用string_helper.dotted_str_to_map取获取它的值,
# 5 把变量前面的文本放入processed_var_list中,然后把变量的值也放进去,等待一会处理
# 5 判断processed_var_list的长度,如果是1,说明${}中只有值,也就是只有a.b.c这样的变量,那么不进行表达式处理
# 6 否则把processed_var_list送入表达式处理模块,执行其中的表达式.并返回表达式的执行结果
def __private_process_one_var(var_str: str, data: map):
    list_func = []

    processed_var_list = []  # 最终输出字符串

    # 选择所有的函数,形如a.b.c(
    for m in regVar_func.finditer(var_str):
        list_func.append(m.group(1))

    last_match_end = 0
    # 遍历所有的变量,形如a.b.c,
    for mv in regVar_var.finditer(var_str):
        matched_var = mv.group(0)  # 内容应该为a.b.c
        if matched_var not in list_func:  # 会把函数x.y.z(中x.y.z这部分也筛选出来,所以要过滤掉.

            # 这个匹配之前的内容加入进来.变量前面什么都没有的时候不加入,方便一会判断是不是有表达式
            if var_str[last_match_end:mv.start(0)] != "":
                processed_var_list.append(Variant(VarTypeEnum.Text, var_str[last_match_end:mv.start(0)]))
            # 开始判断匹配.
            if string_helper.is_number(matched_var) or string_helper.is_keyword(matched_var):
                # 如果是数字or关键字,那么它肯定不是变量,放入结果中待处理
                processed_var_list.append(Variant(VarTypeEnum.Text, matched_var))
            else:
                # 终于找到变量了.将变量转化为data字典中的数据,字典中没有这个数据,返回原文
                processed_var_list.append(Variant(VarTypeEnum.Var, string_helper.dotted_str_to_map(matched_var, data)))

            last_match_end = mv.end(0)
    # 加入结尾的部分,字符串在空的时候不加入,方便一会判断是不是有表达式
    if var_str[last_match_end:] != "":
        processed_var_list.append(Variant(VarTypeEnum.Text, var_str[last_match_end:]))

    if len(processed_var_list) == 1:  # 说明是个单纯的小变量,那么不用执行其中的代码
        return str(processed_var_list[0].content)
    else:
        # 如果判定其中有函数/表达式,对其中的表达式进行处理
        return proc_func.process(processed_var_list, data)


# 处理表达式
def __private_process_express(template: str, data: map):
    express_replacer = ExpressProc()
    express_replacer.process_func = lambda m: __private_process_one_var(m.group(0), data)
    express_replacer.formatStr = "<#if {0}>"
    template = regIfHead.sub(express_replacer.regex_sub_repo, template)

    express_replacer.process_func = lambda m: __private_process_one_var(m.group(0), data)
    express_replacer.formatStr = "<#elseif {0}>"
    template = regElseIf.sub(express_replacer.regex_sub_repo, template)
    return template
